package cn.wen.forum.config;

import cn.wen.forum.ForumApplication;
import cn.wen.forum.common.util.RedisUtils;
import cn.wen.forum.common.util.StringUtils;
import cn.wen.forum.mapper.UserMapper;
import cn.wen.forum.pojo.entity.User;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.*;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.ContextLoader;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

/**
 * @ClassName JWTConfig
 * @Author By wen
 * @Description
 * @Date 2023/3/16 8:58
 * @Version 1.0
 */
public class JWTConfig {

    //常量
    public static final long EXPIRE = 1000 * 60 * 60 * 24; //token过期时间

    // cookies过期时间，1天
    public static final int COOKIES_FAILURE_TIME = 1 * 60 * 60 * 24;

    public static final String APP_SECRET = "SJSjsksdjKSDHJKSDJKSDJKDSU(WDIOPH"; //秘钥，加盐

    /**	@param id 当前用户ID
     *	@param issuer 该JWT的签发者，是否使用是可选的
     *	@param subject 该JWT所面向的用户，是否使用是可选的
     *	@param ttlMillis 什么时候过期，这里是一个Unix时间戳，是否使用是可选的
     *	@param audience 接收该JWT的一方，是否使用是可选的
     *  生成token字符串的方法
     */
    /**
     * 生成jwt
     * @param userKey 用户key，用于获取redis的user
     * @param user 需要直接存在jwt里边的用户信息
     * @return
     */
    public static String getJwtToken(String userKey,User user) {
        String JwtToken = Jwts.builder()
                .setHeaderParam("type", "JWT")    //头部信息
                .setHeaderParam("alg", "HS256")    //头部信息
                //下面这部分是payload部分
                // 设置默认标签
                .setSubject("dailyblue")    //设置jwt所面向的用户
                .setIssuedAt(new Date())    //设置签证生效的时间
                //.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))    //设置签证失效的时间
                //自定义的信息，这里存储id和姓名信息，
                .claim("id", user.getId())  //设置token主体部分 ，存储用户信息
                .claim("name", user.getUserName())
                .claim("headPortrait", user.getHeadPortrait())
                .claim("userKey", userKey)
                //下面是第三部分
                .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                .compact();
        // 生成的字符串就是jwt信息，这个通常要返回出去
        return JwtToken;
    }

    /**
     * 判断token是否存在与有效
     * 直接判断字符串形式的jwt字符串
     *
     * @param jwtToken
     * @return
     */
    public static boolean checkToken(String jwtToken) {
        if (StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 验证token是否有效并且存在用户信息
     * @param token  请求头中携带的token
     * @return  token验证结果  2-token过期；1-token认证通过；0-token认证失败
     */
    public static int verify(String token){
        token = token.substring(token.indexOf(":")+1);
        Claims claims = null;
        try {
            //token过期后，会抛出ExpiredJwtException 异常，通过这个来判定token过期，
            claims = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(token).getBody();
        }catch (ExpiredJwtException e){

        }
        //从token中获取用户id，查询该Id的用户是否存在，存在则token验证通过
        Integer id = Integer.parseInt(String.valueOf(claims.get("id")));
        ApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
//        ApplicationContext ac1 = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
//        ApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(sc);
//        WebApplicationContext wac1 = (WebApplicationContext)
        // springboot中，启动类已经不会跑SpringBootServletInitializer中的onStartup导致ContextLoaderListener没有执行。所以上面无法注册实例对象
        UserMapper userMapper = ForumApplication.ac.getBean(UserMapper.class);
        User user = userMapper.selectById(id);
        if(user != null){
            return 1;
        }else{
            return 0;
        }
    }

    /**
     * 判断token是否合法
     * 因为通常jwt都是在请求头中携带，此方法传入的参数是请求
     *
     *
     * @param request
     * @return
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader("Authorization");//注意名字必须为token才能获取到jwt
            if (StringUtils.isEmpty(jwtToken)) return false;
            jwtToken = jwtToken.substring(jwtToken.indexOf(":")+1);
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据token字符串获取会员id
     * 这个方法也直接从http的请求中获取id的
     *
     * @param request
     * @return
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("Authorization");
        if (StringUtils.isEmpty(jwtToken)) return "";
        jwtToken = jwtToken.substring(jwtToken.indexOf(":")+1);
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String) claims.get("id");
    }

    /**
     * 解析JWT
     * @param jwt
     * @return
     */
    public static Claims parseJWT(String jwt) {
        Claims claims = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwt).getBody();
        return claims;
    }

    /**
     * 解析JWT 获取userKey
     * @param request
     * @return
     */
    public static String parseJWT(HttpServletRequest request) {
        String jwt = request.getHeader("Authorization");
        if (StringUtils.isEmpty(jwt)) return null;
        jwt = jwt.substring(jwt.indexOf(":")+1);
        if (!JWTConfig.checkToken(jwt)) return null;
        Claims claims = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwt).getBody();
        String userKey = (String)claims.get("userKey");
        return userKey;
    }


    /**
     * 根据request对象，获取jwt从redis读取user信息
     * @param request
     * @return
     */
    public static User getUser(HttpServletRequest request, HttpServletResponse response) {
        User user = new User();
        try {
            // 从jwt获取key
            String userKey = JWTConfig.parseJWT(request);
            RedisUtils redisUtils = ForumApplication.ac.getBean(RedisUtils.class);
            Object cacheObject = redisUtils.getCacheObject(userKey);
            user = JSONObject.parseObject(cacheObject.toString(), User.class);
        } catch (Exception e) {
            // 获取不到用户信息，说明用户未登录，则从cookies中获取游客用户标识，如果为null，则以此时的时间戳为value存入，并且区分用户UID，这个时间戳为负值
            // 因为只用来统计浏览量，所以无需考虑高并发下时间戳重复问题
            Long visitorUID = 0l;
            Cookie[] cookies = request.getCookies();
            if (cookies != null) {
                boolean existFlag = false;
                for (Cookie cookie : cookies) {
                    if(StringUtils.equals("visitorUID",cookie.getName())) {
                        visitorUID = Long.parseLong(cookie.getValue());
                        existFlag =true;
                        break;
                    }
                }
                if(!existFlag) {
                    visitorUID = - new Date().getTime();
                    Cookie uidCookie = new Cookie("visitorUID",visitorUID+"");
                    uidCookie.setMaxAge(COOKIES_FAILURE_TIME);
                    response.addCookie(uidCookie);
                }
            }
            user.setId(visitorUID);
        }
        return StringUtils.isNull(user) ? null : user;
    }

    public static void main(String[] args) {
//        User user = new User();
//        user.setId(1l);
//        user.setUserName("user");
//        user.setHeadPortrait("./img/head/img1.png");
//        String jwtToken = "Bearer:" + JWTConfig.getJwtToken("1",user);
//        jwtToken = jwtToken.substring(jwtToken.indexOf(":") +1);
//        System.out.println(jwtToken);
//        System.out.println(JWTConfig.checkToken(jwtToken));
//        System.out.println("用户名称====="+JWTConfig.parseJWT(jwtToken.substring(jwtToken.indexOf(":") +1)).get("name"));
    }

}
